package com.cdn.log.aspect;

import com.cdn.log.anno.Clog;
import com.cdn.log.consts.CLogConst;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @author 蔡定努
 * 2024/06/01 23:21
 */
@Aspect
public class ClogAspect {

    @Autowired
    private ApplicationContext applicationContext;
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    @Around("@annotation(com.cdn.log.anno.Clog)")
    public Object preAuth(ProceedingJoinPoint point) throws Throwable {
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        Object[] args = point.getArgs();
        Clog clog = method.getAnnotation(Clog.class);
        StringBuilder builder = new StringBuilder();
        String[] value = clog.value();
        StandardEvaluationContext context = getEvaluationContext(method, args);
        for (String s : value) {
            Expression expression = EXPRESSION_PARSER.parseExpression(s);
            Object value1 = expression.getValue(context);
            if (null != value1) {
                if (builder.length() > 0) {
                    builder.append(clog.separator());
                }
                builder.append(value1);
            }
        }

        if (builder.length() > 0) {
            builder.insert(0,"|");
        }
        builder.insert(0, MDC.get(CLogConst.TRACE_ID));

        MDC.put(CLogConst.TRACE_ID, builder.toString());
        Object proceed = point.proceed();
        MDC.clear();
        return proceed;
    }


    /**
     * 获取方法上的参数
     *
     * @param method 方法
     * @param args   变量
     * @return {SimpleEvaluationContext}
     */
    private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
        // 初始化Spel表达式上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        // 设置表达式支持spring bean
        context.setBeanResolver(new BeanFactoryResolver(applicationContext));
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            context.setVariable(parameters[i].getName(), args[i]);
        }
        return context;
    }
}
